#pragma once

#include <iostream>
#include <pthread.h>
#include <semaphore.h>
#include <vector>

static const int gcap = 5;

template <class T>
class RingQueue
{
public:
    RingQueue(int cap = gcap):_ring_queue(cap), _cap(cap)
    {
        //初始化信号量
        sem_init(&_space_sem, 0, _cap);
        sem_init(&_data_sem, 0, 0);
        //初始化位置
        _productor_step = 0;
        _consumer_step = 0;
        //初始化信号
        pthread_mutex_init(&_p_mutex, nullptr);
        pthread_mutex_init(&_c_mutex, nullptr);
    }
    void push(const T& in)//由生产者完成
    {
        //对信号量的操作被成为 PV 原语，由两个部分组成的: P, V
        //P: 向临界区申请临界资源，假如申请成功则拥有了对一个临界资源的使用特权，且对应的信号量 -1 
        //失败的话，该线程则会被挂起，直到有线程释放该对应的临界资源，才会继续向下执行操作
        //V: 向临界区释放申请的临界资源，对应的信号量 +1
        //对于生产者和消费者来说，他们看重的临界资源不同
        //生产者看重的是空间资源，而消费者看重的数据资源，所以我们要设置两个不同的信号量:_space_sem, _data_sem
        //在最开始的时候，由于该循环队列上没有任何数据，所以，最开始一定是先向 _space_sem 申请的线程成功并向下继续运行对应的代码
        //所以，_space_sem 的值被初始化为 _cap,_data_sem 被初始化为0
        //循环队列中，当消费者和生产者在同一位置的时候也不需要进行特殊处理
        //该两者在同一位置的时候，有两种情况：为空，为满
        //为空的时候，由于_data_sem为0，所以，只能由生产者先向下执行
        //为满的时候，由于_space_sem为0，所以，只能由消费者者先向下执行
        //对于生产者而言，push的操作，不应该是只有 P 操作，因为生产者没有释放资源的话，那么这个线程则会一直占有对应的资源
        //所以，加上 V 操作，才是完整的 push 操作
        P(_space_sem);
        pthread_mutex_lock(&_p_mutex);
        _ring_queue[_productor_step++] = in;
        _productor_step %= _cap;
        pthread_mutex_unlock(&_p_mutex);
        V(_data_sem);
    }
    void pop(T* out)//由消费者完成
    {
        P(_data_sem);
        pthread_mutex_lock(&_c_mutex);
        *out = _ring_queue[_consumer_step++];
        _consumer_step %= _cap;
        pthread_mutex_unlock(&_c_mutex);
        V(_space_sem);
    }
    ~RingQueue()
    {
        //销毁信号量
        sem_destroy(&_space_sem);
        sem_destroy(&_data_sem);
        //销毁互斥锁
        pthread_mutex_destroy(&_p_mutex);
        pthread_mutex_destroy(&_c_mutex);
    }
private:
    void P(sem_t& sem)
    {
        sem_wait(&sem);
    }
    void V(sem_t& sem)
    {
        sem_post(&sem);
    }
private:
    std::vector<T> _ring_queue;//循环队列
    int _cap;//循环队列的最大容量
    sem_t _space_sem;//空间信号量
    sem_t _data_sem;//数据信号量
    int _productor_step;//生产者的位置
    int _consumer_step;//消费者的位置
    pthread_mutex_t _p_mutex;//生产者的互斥锁
    pthread_mutex_t _c_mutex;//消费者的互斥锁
};